use solutiont7::DistrictData;

enum CardType {
    GenerationOne,
    GenerationTwo,
    Invalid,
}

impl From<usize> for CardType {
    fn from(value: usize) -> Self {
        match value {
            15 => CardType::GenerationOne,
            18 => CardType::GenerationTwo,
            _ => CardType::Invalid,
        }
    }
}

pub fn check_id_card(id_card_no: &str) -> String {
    // Validate length
    let card_type = CardType::from(id_card_no.len());
    match card_type {
        CardType::Invalid => return String::from("身份证号码错误"),
        CardType::GenerationTwo => {
            if !check_sum(id_card_no) {
                return String::from("身份证号码错误");
            }
        }
        _ => {}
    }

    let mut start: usize = 6;
    let mut s = start;
    let year: u32 = match card_type {
        CardType::GenerationOne => {
            start += 2;
            match id_card_no[s..start].parse::<u32>() {
                Ok(val) => 1900 + val,
                Err(_) => return String::from("身份证号码错误"),
            }
        }
        CardType::GenerationTwo => {
            start += 4;
            match id_card_no[s..start].parse() {
                Ok(val) => val,
                Err(_) => return String::from("身份证号码错误"),
            }
        }
        _ => unreachable!(),
    };
    s = start;
    start += 2;
    let month: u32 = match id_card_no[s..start].parse() {
        Ok(val) => val,
        Err(_) => return String::from("身份证号码错误"),
    };
    s = start;
    start += 2;
    let day: u32 = match id_card_no[s..start].parse() {
        Ok(val) => val,
        Err(_) => return String::from("身份证号码错误"),
    };
    if !is_valid_date(year, month, day) {
        return String::from("身份证号码错误");
    }

    let sequence_code_str = match card_type {
        CardType::GenerationOne => &id_card_no[id_card_no.len() - 1..],
        CardType::GenerationTwo => &id_card_no[id_card_no.len() - 2..17],
        _ => unreachable!(),
    };
    let sequence_code: u32 = sequence_code_str.parse().unwrap();
    let sex = if sequence_code % 2 == 0 { "女" } else { "男" };

    let address_code = &id_card_no[0..6];
    let data = DistrictData::new("data");
    let (province, city, district) = match data.search_code(address_code) {
        Some((province, city, district)) => (province, city, district),
        None => {
            return String::from("身份证号码错误");
        }
    };

    let birth_date_formatted = format!("{:04}年{:02}月{:02}日", year, month, day);

    format!(
        "身份证号码正确,{},{},{}-{}-{}",
        sex, birth_date_formatted, province, city, district
    )
}

fn is_valid_date(year: u32, month: u32, day: u32) -> bool {
    if month == 0 || month > 12 || day == 0 || day > 31 {
        return false;
    }
    let is_leap = year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
    let days_in_month = match month {
        2 => {
            if is_leap {
                29
            } else {
                28
            }
        }
        4 | 6 | 9 | 11 => 30,
        _ => 31,
    };
    day <= days_in_month
}

fn check_sum(id_card_no: &str) -> bool {
    let mut digits = [0u32; 17];
    for (i, c) in id_card_no.chars().enumerate() {
        if i < 17 {
            if !c.is_digit(10) {
                return false;
            }
            digits[i] = c.to_digit(10).unwrap();
        } else {
            if !c.is_digit(10) && c != 'X' {
                return false;
            }
        }
    }
    let weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    let check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    let mut sum = 0;
    for i in 0..17 {
        sum += digits[i] * weights[i];
    }
    let modulus = sum % 11;
    let expected_check_char = check_codes[modulus as usize];
    let actual_check_char = id_card_no
        .chars()
        .nth(17)
        .unwrap()
        .to_uppercase()
        .next()
        .unwrap();
    if expected_check_char != actual_check_char {
        return false;
    }
    true
}
